#include "peripheral_com.h"
#include <sys/ioctl.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
#include <termios.h>
#include <string.h>
#include <string>

#include "utils_log.h"

namespace El {
namespace Peripheral {

Com::Com()
{
    devFd_ = -1;
}

Com::~Com(void)
{
    Delete();
}

int32_t Com::Create(const char *dev, bool block)
{
    if (devFd_ > 0) {
        EL_ERROR("Com Create dev({}) failed! Already Created: devFd_ = {}", dev, devFd_);
        return -1;
    }

    EL_INFO("Com Create dev({})! block = {}", dev, block);
    if (block) {
        devFd_ = open(dev, O_RDWR | O_NONBLOCK);
    } else {
        devFd_ = open(dev, O_RDWR | O_NONBLOCK);
    }
    if (devFd_ < 0) {
        EL_INFO("Com Create dev({}) failed! devFd_ = {}", dev, devFd_);
        return -1;
    }
    EL_INFO("IHIReg open dev({}) success! devFd_ ={}", dev, devFd_);
    return devFd_;
}

int32_t Com::Delete(void)
{
    if (devFd_ > 0) {
        close(devFd_);
        devFd_ = -1;
    }
    return 0;
}

int32_t Com::Read(uint8_t *pData, uint32_t bytes)
{
    if (pData == NULL) {
        EL_ERROR("Com: uart_read error->pdata is NULL!");
        return -1;
    } else if (bytes <= 0) {
        EL_ERROR("Com: uart_read error->nbytes must be > 0!");
        return -2;
    }

    int32_t ret = read(devFd_, pData, bytes);
    
    // 打印读取的数据（16进制格式）
    if (ret > 0) {
        std::string hexData;
        for (int32_t i = 0; i < ret; i++) {
            char hexByte[3];
            sprintf(hexByte, "%02X", pData[i]);
            hexData += hexByte;
            if (i < ret - 1) {
                hexData += " ";
            }
        }
        EL_DEBUG("Com::Read received {} bytes: {}", ret, hexData);
    }
    
    return ret;
}

int32_t Com::Write(uint8_t *pData, uint32_t len)
{
    if (pData == NULL) {
        EL_ERROR("Com: reg_write error->pdata is NULL!");
        return -1;
    } else if (len <= 0) {
        EL_ERROR("Com: reg_write error->len must be > 0!");
        return -2;
    }
    // 打印发送的数据（16进制格式）
    std::string hexData;
    for (uint32_t i = 0; i < len; i++) {
        char hexByte[3];
        sprintf(hexByte, "%02X", pData[i]);
        hexData += hexByte;
        if (i < len - 1) {
            hexData += " ";
        }
    }
    EL_DEBUG("Com::Write sending {} bytes: {}", len, hexData);

    int32_t ret = write(devFd_, pData, len);

    return ret;
}

int32_t Com::ReadLen(uint8_t *pData, uint32_t bytes)
{
    int32_t read_ret = 0;
    int32_t count = bytes;
    int32_t cur_pos = 0;
    if (pData == NULL) {
        EL_ERROR("Com: uart_read error->pdata is NULL!");
        return -1;
    } else if (bytes <= 0) {
        EL_ERROR("Com: uart_read error->nbytes must be > 0!");
        return -2;
    }
    while (1) {
        read_ret = read(devFd_, pData + cur_pos, count);
        if (read_ret < 0) {
            return -1;
        } else if (read_ret != count) {
            cur_pos += read_ret;
            count -= read_ret;
        } else {
            break;
        }
    }
    
    // 打印读取的数据（16进制格式）
    std::string hexData;
    for (uint32_t i = 0; i < bytes; i++) {
        char hexByte[3];
        sprintf(hexByte, "%02X", pData[i]);
        hexData += hexByte;
        if (i < bytes - 1) {
            hexData += " ";
        }
    }
    EL_DEBUG("Com::ReadLen received {} bytes: {}", bytes, hexData);
    
    return bytes;
}

int32_t Com::WriteLen(uint8_t *pData, uint32_t len)
{
    int32_t write_ret = 0;
    int32_t count = len;
    int32_t cur_pos = 0;

    if (pData == NULL) {
        EL_ERROR("Com: reg_write error->pdata is NULL!");
        return -1;
    } else if (len <= 0) {
        EL_ERROR("Com: reg_write error->len must be > 0!");
        return -2;
    }

    // 打印发送的数据（16进制格式）
    std::string hexData;
    for (uint32_t i = 0; i < len; i++) {
        char hexByte[3];
        sprintf(hexByte, "%02X", pData[i]);
        hexData += hexByte;
        if (i < len - 1) {
            hexData += " ";
        }
    }
    EL_DEBUG("Com::WriteLen sending {} bytes: {}", len, hexData);

    while (1) {
        write_ret = write(devFd_, pData + cur_pos, count);

        if (write_ret < 0) {
            return -1;
        } else if (write_ret < count) {
            cur_pos += write_ret;
            count -= write_ret;
        } else {
            break;
        }
    }
    return len;
}

int32_t Com::Purge(uint32_t flags)
{
    switch (flags) {
        case UART_PURGE_TXABORT:
            tcflow(devFd_, TCOOFF);
            break;
        case UART_PURGE_RXABORT:
            tcflow(devFd_, TCIOFF);
            break;
        case UART_PURGE_TXCLEAR:
            tcflush(devFd_, TCOFLUSH);
            break;
        case UART_PURGE_RXCLEAR:
            tcflush(devFd_, TCIFLUSH);
            break;
        default:
            EL_INFO("Com:Purge error->Unknow flag = {}", flags);
            return -1;
    }

    return 0;
}

int32_t Com::SetAttribute(UART_ATTR *pattr)
{
    struct termios opt;
    UART_ATTR *attr = pattr;
    if (devFd_ < 0) {
        EL_INFO("Com: SetAttribute error->dev_fd = {}!", devFd_);
        return -1;
    }

    memset(&opt, 0, sizeof(struct termios));
    tcgetattr(devFd_, &opt);
    cfmakeraw(&opt);

    switch (attr->baudrate) {
        case 50:
            cfsetispeed(&opt, B50);
            cfsetospeed(&opt, B50);
            break;
        case 75:
            cfsetispeed(&opt, B75);
            cfsetospeed(&opt, B75);
            break;
        case 110:
            cfsetispeed(&opt, B110);
            cfsetospeed(&opt, B110);
            break;
        case 134:
            cfsetispeed(&opt, B134);
            cfsetospeed(&opt, B134);
            break;
        case 150:
            cfsetispeed(&opt, B150);
            cfsetospeed(&opt, B150);
            break;
        case 200:
            cfsetispeed(&opt, B200);
            cfsetospeed(&opt, B200);
            break;
        case 300:
            cfsetispeed(&opt, B300);
            cfsetospeed(&opt, B300);
            break;
        case 600:
            cfsetispeed(&opt, B600);
            cfsetospeed(&opt, B600);
            break;
        case 1200:
            cfsetispeed(&opt, B1200);
            cfsetospeed(&opt, B1200);
            break;
        case 1800:
            cfsetispeed(&opt, B1800);
            cfsetospeed(&opt, B1800);
            break;
        case 2400:
            cfsetispeed(&opt, B2400);
            cfsetospeed(&opt, B2400);
            break;
        case 4800:
            cfsetispeed(&opt, B4800);
            cfsetospeed(&opt, B4800);
            break;
        case 9600:
            cfsetispeed(&opt, B9600);
            cfsetospeed(&opt, B9600);
            break;
        case 19200:
            cfsetispeed(&opt, B19200);
            cfsetospeed(&opt, B19200);
            break;
        case 38400:
            cfsetispeed(&opt, B38400);
            cfsetospeed(&opt, B38400);
            break;
        case 57600:
            cfsetispeed(&opt, B57600);
            cfsetospeed(&opt, B57600);
            break;
        case 115200:
            cfsetispeed(&opt, B115200);
            cfsetospeed(&opt, B115200);
            break;
        case 230400:
            cfsetispeed(&opt, B230400);
            cfsetospeed(&opt, B230400);
            break;
        case 460800:
            cfsetispeed(&opt, B460800);
            cfsetospeed(&opt, B460800);
            break;
        case 500000:
            cfsetispeed(&opt, B500000);
            cfsetospeed(&opt, B500000);
            break;
        case 576000:
            cfsetispeed(&opt, B576000);
            cfsetospeed(&opt, B576000);
            break;
        case 921600:
            cfsetispeed(&opt, B921600);
            cfsetospeed(&opt, B921600);
            break;
        case 1000000:
            cfsetispeed(&opt, B1000000);
            cfsetospeed(&opt, B1000000);
            break;
        case 1152000:
            cfsetispeed(&opt, B1152000);
            cfsetospeed(&opt, B1152000);
            break;
        case 1500000:
            cfsetispeed(&opt, B1500000);
            cfsetospeed(&opt, B1500000);
            break;
        case 2000000:
            cfsetispeed(&opt, B2000000);
            cfsetospeed(&opt, B2000000);
            break;
        case 2500000:
            cfsetispeed(&opt, B2500000);
            cfsetospeed(&opt, B2500000);
            break;
        case 3000000:
            cfsetispeed(&opt, B3000000);
            cfsetospeed(&opt, B3000000);
            break;
        case 3500000:
            cfsetispeed(&opt, B3500000);
            cfsetospeed(&opt, B3500000);
            break;
        case 4000000:
            cfsetispeed(&opt, B4000000);
            cfsetospeed(&opt, B4000000);
            break;
        default:
            EL_INFO("Com: SetAttribute error->unsupported baudrate {}", attr->baudrate);
            break;
    }

    switch (attr->parity) {
        case UART_NOPARITY:
            opt.c_cflag &= ~PARENB;
            opt.c_iflag &= ~INPCK;
            break;
        case UART_ODDPARITY:
            opt.c_cflag |= PARENB;
            opt.c_cflag |= PARODD;
            opt.c_iflag |= INPCK;
            break;
        case UART_EVENPARITY:
            opt.c_cflag |= PARENB;
            opt.c_cflag &= ~PARODD;
            opt.c_iflag |= INPCK;
            break;
        default:
            EL_INFO("Com: SetAttribute error->unsupported parity {}", attr->parity);
            break;
    }

    opt.c_cflag &= ~CSIZE;
    switch (attr->databits) {
        case 5:
            opt.c_cflag |= CS5;
            break;
        case 6:
            opt.c_cflag |= CS6;
            break;
        case 7:
            opt.c_cflag |= CS7;
            break;
        case 8:
            opt.c_cflag |= CS8;
            break;
        default:
            EL_INFO("Com: SetAttribute error->unsupported data bits {}", attr->databits);
            break;
    }

    opt.c_cflag &= ~CSTOPB;
    switch (attr->stopbits) {
        case 0:
        case 1:
            opt.c_cflag &= ~CSTOPB;
            break;

        case 2:
            opt.c_cflag |= CSTOPB;
            break;
        default:
            EL_INFO("Com: SetAttribute error->unsupported stop bits {}", attr->stopbits);
            opt.c_cflag &= ~CSTOPB;
            break;
    }
    opt.c_cc[VTIME] = 0;
    opt.c_cc[VMIN] = 1;

    tcflush(devFd_, TCIOFLUSH);
    if (tcsetattr(devFd_, TCSANOW, &opt) < 0) {
        EL_ERROR("Com: SetAttribute error->tcsetattr");
        return -1;
    }

    return 0;
}

int32_t Com::GetAttribute(void)
{
    int32_t uart_fd = devFd_;
    struct termios opt;

    if (uart_fd < 0) {
        EL_ERROR("Com: GetAttribute error->dev_fd = {}!", uart_fd);
        return -1;
    }

    memset(&opt, 0, sizeof(struct termios));
    tcgetattr(uart_fd, &opt);
    cfmakeraw(&opt);
    return 0;
}

} // namespace Peripheral
} // namespace El
